
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ContentOverContainer.Common;
using ContentOverContainer.Common.Converter;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Compilers.Common;
using Roslyn.Services;
using Roslyn.Services.Formatting;

namespace ContentOverContainer
{



    /// <summary>
    /// Builds a list of method declaration nodes annotated with the MultiplicityAttribute together with their replacements
    /// and a list of their references.
    /// it takes care that fields or property references used in the methods are already reaplaced.
    /// </summary>
    /// <author>Harald Binkle</author>
    public class MethodWalker : BaseWalker
    {
        public MethodWalker(ISolution pSolution, ReferencesList pReferences, ChangeNodeInfoList pChangeNodePairs) : base(pSolution, pReferences, pChangeNodePairs)
        {
        }

        public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            MethodDeclarationSyntax syntaxNode = node;
            if (syntaxNode == null) return;

            var newnode = node;
            var newParameterNode = VisitMethodParameters(newnode);

            var multiplicityAtt = node.AttributeLists.GetMultiplicityAttributes();
            if (multiplicityAtt == null)
            {
                if (newParameterNode != null)
                    m_Replacements.Add(node, newParameterNode, m_Document);
            }
            else
            {
                var multiInfo = multiplicityAtt.GetMultiplicityInfo(node.ReturnType);

                //var references = node.FindAllReferences(m_SemModel, this.m_Solution).Cast<SyntaxNode>();
                //m_References.Add(references, multiInfo, m_Document);
                var returnStatements = node.Body.DescendantNodes().Where(x => x is ReturnStatementSyntax);
                m_References.Add(returnStatements, multiInfo, m_Document);

                if (newParameterNode != null)
                    newnode = newParameterNode;

                if (multiInfo.Type != null)
                    newnode = newnode.ChangeReturnTypeToMultiplicityType(multiInfo.Type);
                var changeNode = newnode.RemoveMultiplicityAttribute(newnode.AttributeLists, true);
                m_Replacements.Add(node, changeNode, m_Document);
            }
        }

        private MethodDeclarationSyntax VisitMethodParameters(MethodDeclarationSyntax node)
        {
            MethodDeclarationSyntax newNode = node;
            //process Attributes of parameters
            var newParamList = Syntax.ParameterList();
            var found = false;
            foreach (var parameter in node.ParameterList.Parameters)
            {
                var multiplicityAtt = parameter.AttributeLists.GetMultiplicityAttributes();
                if (multiplicityAtt == null)
                {
                    newParamList = newParamList.AddParameters(parameter);
                    continue;
                }

                found = true;
                var multiInfo = multiplicityAtt.GetMultiplicityInfo(parameter.Type);
                var newParam = parameter.ChangeToMultiplicityType(multiInfo.Type);
                newParam = (ParameterSyntax)newParam.RemoveMultiplicityAttribute(newParam.AttributeLists, true);
                newParamList = newParamList.AddParameters(newParam);


                var references = parameter.FindAllReferences(m_SemModel, this.m_Solution).Cast<SyntaxNode>();
                m_References.Add(references, multiInfo, m_Document);
            }
            if (found)
                return newNode.WithParameterList(newParamList);
            else return null;
        }
    }
}
